import * as THREE from "three";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { TWEEN } from "three/examples/jsm/libs/tween.module.min.js";
import { getDatasByIndex } from "./modelDB.js";
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js'
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";
import { CSS3DRenderer, CSS3DSprite } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
export default class PmClass {
  constructor() {
    this.container = document.getElementById("container");
    this.scene = null;
    this.camera = null;
    this.renderer = null;
    this.renderer2 = null;
    this.cameraLabel = null;
    this.controls = null;
    this.requestId = null;
    this.composer = null;
    this.outlinePass = null;
    this.css3Dflag = false;
    this.fpqModel = null;
    this.init();
  }
  init() {
    this.initScene();
    this.initLight();
    this.initCamera();
    this.initRender();
    this.initControls();
    window.onresize = () => {
      this.onWindowResize();
    };
    document.oncontextmenu = e => {
      e.preventDefault();
    };
  }
  change3dLabel(position) {
    if (!this.renderer2) {
      this.scene2 = new THREE.Scene();
      this.renderer2 = new CSS3DRenderer();
      this.renderer2.setSize(this.container.clientWidth,
        this.container.clientHeight);
      this.renderer2.domElement.style.position = 'absolute';
      this.renderer2.domElement.style.left = 0;
      this.renderer2.domElement.style.top = 0;
      this.renderer2.domElement.style.right = 0;
      this.renderer2.domElement.style.bottom = 0;
      this.renderer2.domElement.style.pointerEvents = 'none'
      this.container.appendChild(this.renderer2.domElement);
    }
    this.css3Dflag = true;
    if (!this.cameraLabel) {
      const element = document.getElementById('cameraLabel');
      this.cameraLabel = new CSS3DSprite(element);
      this.cameraLabel.position.set(position[0], position[1], position[2]);
      this.cameraLabel.scale.set(0.05, 0.05, 0.05);
      this.scene2.add(this.cameraLabel);
    } else {
      !this.cameraLabel.visible && (this.cameraLabel.visible = true);
      this.cameraLabel.position.set(position[0], position[1], position[2]);
    }
    this.flyAnimation({ position: [position[0] - 30, position[1] + 10, position[2], position[0], position[1], position[2]] });
  }
  changeCamera(obj) {
    if (obj.id == 'JK13' || obj.id == 'JK14') {
      this.scene.children[1].children.some((item) => {
        if (item.name == '分配器') {
          item.visible = true;
          return true;
        }
      })
    }
    this.change3dLabel(obj.position);
  }
  changeSbModel(name, flag = true) {
    if (flag) {
      let _p = new THREE.Vector3();
      this.scene.children[1].children.forEach((item) => {
        if (item.name == name) {
          item.visible = true;
          item.getWorldPosition(_p);
        } else {
          item.visible = false;
        }
      })
      this.flyAnimation({ position: [_p.x - 25, _p.y + 2, _p.z - 25, _p.x, _p.y, _p.z] });
    } else {
      this.scene.children[1].children.forEach((item) => {
        if (item.name != '分配器') {
          item.visible = true;
        }
      })
    }
  }
  changeModel(obj, flag = false) {
    if (flag) {
      this.css3Dflag = false;
      this.fpqModel.visible = false;
      this.outlinePass && (this.outlinePass.selectedObjects = []);
      this.cameraLabel && (this.cameraLabel.visible = false);
      this.renderer2 && this.renderer2.render(this.scene2, this.camera);
    } else {
      !this.outlinePass && (this.initOutlinePass())
      if (obj.id == "3-11") {
        this.fpqModel.visible = true;
        obj.mesh = this.fpqModel;
      } else {
        let _name = obj.name.replace('N₂', 'N2');
        obj.mesh = this.scene.getObjectByName(_name);
      }
    }
    this.flyAnimation(obj);
  }
  roamAnimate(flag = true) {
    if (!flag) {
      TWEEN.removeAll();
      this.controls.enabled = true;
      this.sceneInit();
      return
    }
    this.controls.enabled = false;
    this.camera.position.set(135.195, 51.874, 134.036);
    this.camera.lookAt(-10, 28, 22);
    new TWEEN.Tween(this.camera.position).to({
      x: 97.831,
      y: 74.916,
      z: -84.368
    }, 8000).onUpdate((obj) => {
      this.camera.position.set(obj.x, obj.y, obj.z)
      this.camera.lookAt(-10, 28, 22);
    }).onComplete(() => {
      new TWEEN.Tween(this.camera.position).to({
        x: -136.631,
        y: 52.516,
        z: 15.393,
      }, 6000).onUpdate((obj) => {
        this.camera.position.set(obj.x, obj.y, obj.z)
        this.camera.lookAt(-10, 28, 22);
      }).onComplete(() => {
        this.controls.enabled = true;
      }).start()
    }).start()
  }
  flyAnimation(obj, time = 1000) {
    new TWEEN.Tween({
      x1: this.camera.position.x,
      y1: this.camera.position.y,
      z1: this.camera.position.z,
      x2: this.controls.target.x,
      y2: this.controls.target.y,
      z2: this.controls.target.z
    }).to({
      x1: obj.position[0],
      y1: obj.position[1],
      z1: obj.position[2],
      x2: obj.position[3],
      y2: obj.position[4],
      z2: obj.position[5]
    }, time)
      .easing(TWEEN.Easing.Linear.None)
      .onUpdate(obj => {
        this.camera.position.set(obj.x1, obj.y1, obj.z1);
        this.controls.target.set(obj.x2, obj.y2, obj.z2);
        this.controls.update();
      })
      .onStart(() => {
        this.controls.enabled = false;
      })
      .onComplete(() => {
        this.controls.enabled = true;
        if (obj.mesh) {
          this.outlinePass.selectedObjects = [obj.mesh];
        }
      })
      .start();
  }
  initModel() {
    let that = this;
    return new Promise((resolve, reject) => {
      getDatasByIndex(`../../static/models/pm.glb`, 2).then(res => {
        const progressElement = document.getElementById("progress");
        if (res) {
          const mUrl = URL.createObjectURL(new Blob([res.value]));
          const manager = new THREE.LoadingManager();
          manager.onLoad = () => {
            progressElement.style.display = 'none';
            that.sceneInit();
            that.animate();
            resolve(true);
          };
          const loader = new GLTFLoader(manager);
          loader.load(mUrl, (gltf) => {
            gltf.scene.children.some((item) => {
              if (item.name == '分配器') {
                item.visible = false;
                that.fpqModel = item;
                return true;
              }
            })
            that.scene.add(gltf.scene);
            URL.revokeObjectURL(mUrl);
          }, (xhr) => {
            let _loadPercent = parseInt((xhr.loaded * 100) / xhr.total);
            if (_loadPercent > 90) {
              progressElement.innerText = `加速渲染中...`;
            } else {
              progressElement.innerText = `努力加载中... ${_loadPercent}%`;
            }
          });
        } else {
          progressElement.innerText = `模型加载失败，请刷新 ！`;
          resolve(false);
        }
      });
    })
  }
  sceneInit() {
    this.changeModel({ position: [-105, 38, -55, -10, 28, 22] }, true);
  }
  animate() {
    // console.log(this.camera.position)
    // console.log(this.controls.target)
    // console.log('--------')
    TWEEN.update();
    if (this.composer) {
      this.composer.render();
    } else {
      this.renderer.render(this.scene, this.camera);
    }
    if (this.css3Dflag) {
      this.renderer2.render(this.scene2, this.camera);
    }
    this.requestId = requestAnimationFrame(() => {
      this.animate();
    });
  }
  cloneModels(model) {
    const _model = model.clone();
    if (_model.type == "Mesh") {
      _model.material = model.material.clone();
    } else {
      _model.traverse((item) => {
        if (item.type == "Mesh") {
          item.material = item.material.clone();
        }
      });
    }
    return _model;
  }
  delModels(models) {
    models.forEach(function (model) {
      if (model.parent !== null) {
        model.parent.remove(model);
        if (model.geometry) {
          model.geometry.dispose();
        }
        if (model.material) {
          model.material.dispose();
        }
      }
    });
  }
  initOutlinePass() {
    this.composer = new EffectComposer(this.renderer);
    let renderPass = new RenderPass(this.scene, this.camera);
    this.composer.addPass(renderPass);
    var effectFXAA = new ShaderPass(FXAAShader);
    effectFXAA.uniforms.resolution.value.set(1 / this.container.clientWidth, 1 / this.container.clientHeight);
    effectFXAA.renderToScreen = true;
    this.composer.addPass(effectFXAA);
    this.outlinePass = new OutlinePass(
      new THREE.Vector2(
        this.container.clientWidth,
        this.container.clientHeight
      ),
      this.scene,
      this.camera
    );
    this.outlinePass.edgeStrength = 10.0;
    this.outlinePass.edgeGlow = 1.0;
    this.outlinePass.usePatternTexture = false;
    this.outlinePass.edgeThickness = 4.0;
    this.outlinePass.downSampleRatio = 1.0;
    this.outlinePass.pulsePeriod = 3;
    this.outlinePass.visibleEdgeColor.set("#3FDAFD");
    this.outlinePass.hiddenEdgeColor = new THREE.Color(0, 0, 0);
    this.outlinePass.clear = true;
    this.composer.addPass(this.outlinePass);
  }
  initScene() {
    this.scene = new THREE.Scene();
    // this.scene.add(new THREE.AxesHelper(50));
  }
  initLight() {
    let group = new THREE.Group();
    group.name = "light-group";
    const light = new THREE.DirectionalLight(0xffffff, 2.0);
    light.position.set(1, 1, 1);
    group.add(light);
    const light1 = new THREE.DirectionalLight(0xffffff, 2.0);
    light1.position.set(-1, 1, 0);
    group.add(light1);
    const light2 = new THREE.DirectionalLight(0xffffff, 1.0);
    light2.position.set(0, -1, 0);
    group.add(light2);
    const light3 = new THREE.DirectionalLight(0xffffff, 1.2);
    light3.position.set(0, 0, -1);
    group.add(light3);
    this.scene.add(group);
  }
  initCamera() {
    this.camera = new THREE.PerspectiveCamera(
      55,
      this.container.clientWidth / this.container.clientHeight,
      0.1,
      1000
    );
  }
  initRender() {
    this.renderer = new THREE.WebGLRenderer({
      antialias: true,
      // logarithmicDepthBuffer: true,
      alpha: true
    });
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.setSize(
      this.container.clientWidth,
      this.container.clientHeight
    );
    this.renderer.outputEncoding = THREE.sRGBEncoding;
    // this.renderer.toneMappingExposure = 1.0;
    // this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
    this.container.appendChild(this.renderer.domElement);
  }
  initControls() {
    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    // this.controls.minPolarAngle = 0.2;
    // this.controls.maxPolarAngle = 1.4;
    this.controls.minDistance = 1;
    this.controls.maxDistance = 800;
  }
  onWindowResize() {
    this.camera.aspect =
      this.container.clientWidth / this.container.clientHeight;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(
      this.container.clientWidth,
      this.container.clientHeight
    );
    if (this.renderer2) {
      this.renderer2.setSize(
        this.container.clientWidth,
        this.container.clientHeight
      );
    }
  }
  disposeChild(mesh) {
    if (mesh instanceof THREE.Mesh) {
      if (mesh.geometry) {
        mesh.geometry.dispose();
      }
      if (mesh.material.texture) {
        mesh.material.texture.dispose();
      }
      if (mesh.material) {
        mesh.material.dispose();
      }
    }
    if (mesh instanceof THREE.Group) {
      mesh.clear();
    }
    if (mesh instanceof THREE.Object3D) {
      mesh.clear();
    }
  }
  destroy() {
    if (this.requestId) {
      cancelAnimationFrame(this.requestId);
      this.requestId = null;
    }
    this.scene.traverse(item => {
      this.disposeChild(item);
    });
    this.scene.clear();
    this.scene = null;
    if (this.scene2) {
      this.scene2.traverse(item => {
        this.disposeChild(item);
      });
      this.scene2.clear();
      this.scene2 = null;
    }
    this.camera = null;
    if (this.outlinePass) {
      this.outlinePass.dispose();
      this.outlinePass = null;
    }
    this.composer && (this.composer = null);
    TWEEN.removeAll();
    if (this.renderer) {
      this.renderer.dispose();
      this.renderer.forceContextLoss();
      this.renderer.domElement = null;
      this.renderer.content = null;
      this.renderer = null;
    }
    if (this.renderer2) {
      this.renderer2.domElement = null;
      this.renderer2.content = null;
      this.renderer2 = null;
    }
    window.onresize = null;
    window.oncontextmenu = null;
  }
}
